iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Modern Web

從Vue學React!不只要會用,還要真的懂~系列 第 24

【Day 24】跨越直系與旁系元件的全域狀態管理工具

  • 分享至 

  • xImage
  •  

針對狀態管理,前面有看過最基本的useState,也有看過useReducer用法,甚至還有搭配useContext,讓state可以不用再層層傳遞,但是目前為止state都只能直系傳遞,也就是只能傳遞給自己層級以下的元件,如果是兩個放在同一層的元件,就無法互相傳遞狀態。但總還是會有一些情境,會需要全域傳遞,這時候前面提到過的hooks都無法達到這個目的,只能另外透過其他Libray達到這個目的,今天要看的也就是可以於全域環境下傳遞狀態的狀態管理Libray。

與Vuex一樣都是全域狀態管理工具的Redux

在Vue的世界裡,講到管理全域狀態的工具大家一定都會想到Vuex,在Vue3之後,官方主推的是另外一套叫做Pinia的Libray,所以在透過CLI創建專案時,會是問要不要使用Pinia。雖然它與Vuex只有寫法上有差異,不過整體概念其實一樣。而到了React的世界,想要全域管理狀態則是使用Redux。這裡要特別強調一下,Redux雖然跟React名字很相像,但Redux並不只限於使用在React上,相反的,Vuex則是專門為了Vue所設計的狀態管理工具
接著來了解一下Vuex和Redux對於資料操作的整體流程是什麼?

Vuex

https://ithelp.ithome.com.tw/upload/images/20230930/20130914lyRni3TMJC.png
在Vuex的狀態管理機制下,會統一將state保管在store裡面,如果有非同步的請求時,會先dispatch一個action,再透過commit mutaion來更新state。如果是同步的動作,則可以直接透過commit一個mutaion來更新state。另外,雖然需要更新state時,可以直接對state進行改動,不過為了讓改動比較好追蹤,還是建議一律使用mutation的方式更新state,不過這樣還是在mutable特性下的改動,只會被包裝成mutation函式,且必須透過commit來觸發。

Redux

https://i.imgur.com/unnU4RJ.gif
在Redux的狀態管理機制下,一樣會將state管理在store裡,並且會透過reducer定義變更state的動作有哪些,如果要更新state時,會dispatch一個action,再依照reducer定義的action type決定要進行什麼內容的state的更新。這裡的reducer與前面也提到的useReducer接收action來更新state的部分類似。

從Vuex flow中的主要角色和操作對照React flow角色和操作

前面快速看了Vuex和React對於state操作整體流程後,其中有幾個在流程中很像角色和動作,這裡也來稍微做個對比。

store

Vuex和Redux的store都是用來集中管理state和state相關操作的地方,也可以用來進行模組化的管理。例如和account相關的state和更新、操作state的動作都可以管理在accountStore。

state

Vuex會在state區塊中定義Vuex管理的state;Redux則是透過把初始state傳入reducer,來帶入來定義全域的state。除此之外,還會透過reducer定義不同的action type來告知Redux要怎麼更新state。

在Vuex中,透過state區塊定義要管理的state

const store = createStore({
  state () {
    return {
      count: 0
    }
  }
})

在Redux中,會先透過把初始值傳入reducer來定義要管理的state,以及定義更新state的action type。

const initialState = {
  count: 0,
};
const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_COUNT':
      return {
        ...state,
        count: state.count + 1,
      };
    default:
      return state;
  }
};

const store = createStore(reducer);

mutation

在Vuex裡,mutation函式是用來更新state的函式,mutation函式會被定義在mutations的區塊,並且透過commit觸發;如果是Redux的話,會把更新state的動作也定義在Reducer裡面,要更新state時,則會透過dispatch一個action來讓reducer根據action type進行相對應的state的更新。

Vuex會這樣把更新state的函式都管理在mutation的區塊。

const store = createStore({
  state () {
    // 略...
  },
  mutations: {
    addCount (state) {
      state.count++
    }
  }
})

// 使用時
commit('addCount')

Redux則是透過Reducer定義更新state的action type。

const reducer = (state = initialState, action) => {
  switch (action.type) {
    case 'ADD_COUNT':
      return {
        ...state,
        count: state.count + 1,
      };
    case 'MINUS_COUNT':
      return {
        ...state,
        count: state.count - 1,
      };
    default:
      return state;
  }
};

// 創建store後,就可以dispatch action
store.dispatch({ type: 'ADD_COUNT' });

action

在Vuex裡的action是一個函式,主要是用來處理非同步的操作,且會被定義在actions的區塊。在使用時,是透過dispatch來觸發;Redux的action是一個物件,通常包含type的這個屬性。在實務中,通常會透過action creator回傳action,這個action會用來描述要執行什麼樣的state操作。實際使用時,則會用dispatch把action發送到reducer中,進行state的操作。

Vuex會把action

const store = createStore({
  state () {
    // 略...
  },
  mutations: {
    // 略...
  },
  actions: {
    addCountAction(context) {
      // 在action中,可以透過context這個參取得commit,這樣就可以將更新值更新到store裡面
      context.commit('addCount');
    }  
  },
})

Redux會透過action creator的函式回傳一個action物件,再透過dispatch傳到reducer裡面更新state。

// action creator回傳action
const addCount = () => {
  return {
    type: 'ADD_COUNT',
  };
};

// 實際使用時
store.dispatch(addCount());

getter - 快速取得state的方式

Vuex可以透過getter來取得的一些處理過的state,這些處理過的state會被放在getters區塊;Redux本身沒有getter的這個用法,但是可以透過自己建立selector函式來讓實作時可以快速取得要的state,如果搭配React-Redux使用,則可以用useSelector這個hook來取得state。
Vuex

const store = createStore({
  state () {
    // 略...
  },
  mutations: {
    // 略...
  },
getters: {
  // 取得總金額的getter
  totalPrice: (state) => {
    return state.cart.reduce((total, product) => {
      return total + product.price * product.quantity;
    }, 0);
  }
}
})

Redux

const { cartItemList } = useSelector(state => state.shoppingCartReducer);

發起一個改動state的請求-commit & dispatch

Vuex要改動store裡的state時,會透過commit觸發mutation;如果Redux要改動store的state時,則是透過dispatch帶入action。
Vuex

store.commit('addCount');

Redux

dispatch({ type: "ADD_COUNT" }); 

處理非同步的操作

Vuex會把處理非同步的操作放在action區塊管理,並用dispatch觸發action;雖然Redux也可以透過發送action來處理非同步的操作,但如果手動處理非同步操作的話,需要自己額外去管理非同步動作的開始、結束,以及錯誤的狀態,所以通常會透過Redux Thunk或Redux Sage這些middleware套件來處理,讓這些非同步操作的狀態可以被管理在一起。如果是使用React Toolkit,也可以用更便利的方式,也就是React Toolkit提供的RTK Query來處理非同步的操作。這部分也會在後面實作的時候,再進一步地了解。

今天快速地從Vuex延伸到Redux,以及從比較Vuex和Redux flow中的一些角色和動作,來進一步熟悉它們管理及更新store內state的流程,明天會接著透過實作的方式,來熟悉在React中如何使用Redux。

參考資料

What is Vuex
Redux Essentials


上一篇
【Day 23】利用useReducer + useContext管理複雜的狀態邏輯
下一篇
【Day 25】用實作學Redux Toolkit!完成一個購物車(上) - 基本設定&基本用法
系列文
從Vue學React!不只要會用,還要真的懂~30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言